﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Chart2D
{
    class CoordinateGrid
    {
        static private void DrawArrow(Graphics g, PointF point, int direct)
        {
            const float c_r = 4;
            const float c_h = 14;

            PointF[] points = new PointF[3];

            if (direct == 0) // горизонтальная стрелка
            {
                points[0] = new PointF(point.X, point.Y - c_r);
                points[1] = new PointF(point.X + c_h, point.Y);
                points[2] = new PointF(point.X, point.Y + c_r);
            }
            else // вертикальная стрелка
            {
                points[0] = new PointF(point.X - c_r, point.Y);
                points[1] = new PointF(point.X, point.Y - c_h);
                points[2] = new PointF(point.X + c_r, point.Y);
            }

            g.FillPolygon(Brushes.Black, points);
        }

        static private void DecartGrid(Graphics g, RectangleF boundRectWin, double MinX, double MaxX, double MinY, double MaxY)
        {
            PointF zero = Transformer.ConvertToWin(new DoublePrecisionPoint(0, 0), boundRectWin, MinX, MaxX, MinY, MaxY);
            float step = Math.Min(boundRectWin.Width, boundRectWin.Height) / 4;

            PointF point1 = new PointF();
            PointF point2 = new PointF();

            // отрисовываем boundRectWin
            g.DrawRectangle(Pens.LightGray, boundRectWin.Left, boundRectWin.Top, boundRectWin.Width, boundRectWin.Height);
            RectangleF rb = new RectangleF(boundRectWin.X, boundRectWin.Y, boundRectWin.Width, boundRectWin.Height);
            rb.Inflate(1, 1);

            // отрисовываем габаритные значения
            Font font = new Font("Verdana", 8.0f);

            // left-top
            string script = string.Format("({0}; {1})", MinX.ToString("0.##"), MaxY.ToString("0.##"));
            SizeF sizeScript = g.MeasureString(script, font);
            g.DrawString(script, font, Brushes.Black, boundRectWin.X - sizeScript.Width / 2, boundRectWin.Y - sizeScript.Height);

            // right-top
            script = string.Format("({0}; {1})", MaxX.ToString("0.##"), MaxY.ToString("0.##"));
            sizeScript = g.MeasureString(script, font);
            g.DrawString(script, font, Brushes.Black, boundRectWin.Right - sizeScript.Width / 2, boundRectWin.Y - sizeScript.Height);

            // right-bottom
            script = string.Format("({0}; {1})", MaxX.ToString("0.##"), MinY.ToString("0.##"));
            sizeScript = g.MeasureString(script, font);
            g.DrawString(script, font, Brushes.Black, boundRectWin.Right - sizeScript.Width / 2, boundRectWin.Bottom + sizeScript.Height);

            // left-bottom
            script = string.Format("({0}; {1})", MinX.ToString("0.##"), MinY.ToString("0.##"));
            sizeScript = g.MeasureString(script, font);
            g.DrawString(script, font, Brushes.Black, boundRectWin.X - sizeScript.Width / 2, boundRectWin.Bottom + sizeScript.Height);

            if (rb.Contains(zero))
            {
                // отрисовываем горизонтальные линии
                point1.Y = point2.Y = zero.Y - step;
                point1.X = boundRectWin.Left;
                point2.X = boundRectWin.Right;

                // вверх от 0.
                while (point1.Y > boundRectWin.Top)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.Y -= step;
                    point2.Y -= step;
                }

                // вниз от 0.
                point1.Y = point2.Y = zero.Y + step;
                point1.X = boundRectWin.Left;
                point2.X = boundRectWin.Right;

                while (point1.Y < boundRectWin.Bottom)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.Y += step;
                    point2.Y += step;
                }

                // отрисовываем вертикальные линии
                point1.X = point2.X = zero.X - step;
                point1.Y = boundRectWin.Top;
                point2.Y = boundRectWin.Bottom;

                // влево от 0.
                while (point1.X > boundRectWin.Left)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.X -= step;
                    point2.X -= step;
                }

                // вправо от 0.
                point1.X = point2.X = zero.X + step;
                point1.Y = boundRectWin.Top;
                point2.Y = boundRectWin.Bottom;

                while (point1.X < boundRectWin.Right)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.X += step;
                    point2.X += step;
                }

                Pen axePen = new Pen(Color.Black, 2.0f);

                // отрисовываем ось Y
                point1 = new PointF(zero.X, boundRectWin.Top);
                point2 = new PointF(zero.X, boundRectWin.Bottom);

                g.DrawLine(axePen, point1, point2);
                DrawArrow(g, point1, 1);

                // отрисовываем ось X
                point1 = new PointF(boundRectWin.Left, zero.Y);
                point2 = new PointF(boundRectWin.Right, zero.Y);

                g.DrawLine(axePen, point1, point2);
                DrawArrow(g, point2, 0);
            }
            else if (boundRectWin.Left < zero.X && zero.X < boundRectWin.Right)
            {
                // отрисовываем горизонтальные линии
                point1.Y = point2.Y = boundRectWin.Bottom - step;
                point1.X = boundRectWin.Left;
                point2.X = boundRectWin.Right;

                // вверх от 0.
                while (point1.Y > boundRectWin.Top)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.Y -= step;
                    point2.Y -= step;
                }

                // отрисовываем вертикальные линии
                point1.X = point2.X = zero.X;
                point1.Y = boundRectWin.Top;
                point2.Y = boundRectWin.Bottom;

                // влево от 0.
                while (point1.X > boundRectWin.Left)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.X -= step;
                    point2.X -= step;
                }

                // вправо от 0.
                point1.X = point2.X = zero.X + step;
                point1.Y = boundRectWin.Top;
                point2.Y = boundRectWin.Bottom;

                while (point1.X < boundRectWin.Right)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.X += step;
                    point2.X += step;
                }

                Pen axePen = new Pen(Color.Black, 2.0f);

                // отрисовываем ось Y
                point1 = new PointF(zero.X, boundRectWin.Top);
                point2 = new PointF(zero.X, boundRectWin.Bottom);

                g.DrawLine(axePen, point1, point2);
                DrawArrow(g, point1, 1);
            }
            else if (boundRectWin.Top < zero.Y && zero.Y < boundRectWin.Bottom)
            {
                // отрисовываем горизонтальные линии
                point1.Y = point2.Y = zero.Y - step;
                point1.X = boundRectWin.Left;
                point2.X = boundRectWin.Right;

                // вверх от 0.
                while (point1.Y > boundRectWin.Top)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.Y -= step;
                    point2.Y -= step;
                }

                // вниз от 0.
                point1.Y = point2.Y = zero.Y + step;
                point1.X = boundRectWin.Left;
                point2.X = boundRectWin.Right;

                while (point1.Y < boundRectWin.Bottom)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.Y += step;
                    point2.Y += step;
                }

                // отрисовываем вертикальные линии
                point1.X = point2.X = boundRectWin.Right;
                point1.Y = boundRectWin.Top;
                point2.Y = boundRectWin.Bottom;

                // влево от 0.
                while (point1.X > boundRectWin.Left)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.X -= step;
                    point2.X -= step;
                }

                Pen axePen = new Pen(Color.Black, 2.0f);

                // отрисовываем ось X
                point1 = new PointF(boundRectWin.Left, zero.Y);
                point2 = new PointF(boundRectWin.Right, zero.Y);

                g.DrawLine(axePen, point1, point2);
                DrawArrow(g, point2, 0);
            }
            else
            {
                // отрисовываем горизонтальные линии
                point1.Y = point2.Y = boundRectWin.Bottom - step;
                point1.X = boundRectWin.Left;
                point2.X = boundRectWin.Right;

                // вверх
                while (point1.Y > boundRectWin.Top)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.Y -= step;
                    point2.Y -= step;
                }

                // отрисовываем вертикальные линии
                point1.X = point2.X = boundRectWin.Right - step;
                point1.Y = boundRectWin.Top;
                point2.Y = boundRectWin.Bottom;

                // вправо
                while (point1.X > boundRectWin.Left)
                {
                    g.DrawLine(Pens.LightGray, point1, point2);
                    point1.X -= step;
                    point2.X -= step;
                }
            }
        }

        static private void PolarGrid(Graphics g, RectangleF boundRectWin)
        {
            // рисуем концентрические окружности
            const int c_circle_count = 4;
            float r = Math.Min(boundRectWin.Width, boundRectWin.Height) / 2;
            float xCenter = boundRectWin.X + boundRectWin.Width / 2;
            float yCenter = boundRectWin.Y + boundRectWin.Height / 2;

            for (int i = 0; i < c_circle_count; i++)
            {
                float radius = r - r / c_circle_count * i;

                RectangleF rectangleFs = new RectangleF
                {
                    X = xCenter - radius,
                    Y = yCenter - radius,
                    Width = 2 * radius,
                    Height = 2 * radius
                };

                g.DrawEllipse(Pens.LightGray, rectangleFs);
            }

            // рисуем центральные линии и градусы
            const int c_center_line = 12;
            const double c_angle = 2 * Math.PI / c_center_line;
            const float c_shift = 8;

            PointF startPoint = new PointF(xCenter, yCenter);
            string[] labels = new string[] { "E", "30°", "60°", "N", "120°", "150°", "W", "210°", "240°", "S", "300°", "330°" };
            Font fontNum = new Font("Verdana", 9.0f);
            Font fontLetter = new Font("Verdana", 9.0f, FontStyle.Bold | FontStyle.Italic);
            PointF point;

            for (int i = 0; i < c_center_line; i++)
            {
                double co = Math.Cos(c_angle * i);
                double si = Math.Sin(c_angle * i);

                double xr = co * (r + c_shift) + xCenter;
                double yr = -si * (r + c_shift) + yCenter;

                point = new PointF(Convert.ToSingle(xr), Convert.ToSingle(yr));
                g.DrawLine(Pens.LightGray, startPoint, point);

                SizeF sizeLabel = g.MeasureString(labels[i], fontNum);

                xr = co * (r + c_shift + 1 + sizeLabel.Width / 2) + xCenter;
                yr = -si * (r + c_shift + 1 + sizeLabel.Width / 2) + yCenter;

                point = new PointF(Convert.ToSingle(xr) - sizeLabel.Width / 2, Convert.ToSingle(yr) - sizeLabel.Height / 2);
                g.DrawString(labels[i], (i % 3 == 0) ? fontLetter : fontNum, Brushes.Black, point);
            }
        }

        static public void Render(Graphics g, RectangleF boundRectWin, double MinX, double MaxX, double MinY, double MaxY, bool bPolar)
        {
            if (bPolar)
            {
                PolarGrid(g, boundRectWin);
            }
            else
            {
                DecartGrid(g, boundRectWin, MinX, MaxX, MinY, MaxY);
            }
        }
    }
}
